package pikasial.utils;

import pikasial.fs.Dossier;
import pikasial.images.AlbumPhotos;
import pikasial.images.ImageContainer;
import pikasial.images.ListeAlbums;
import pikasial.images.PikImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.Date;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import pikasial.Configuration;
import pikasial.fs.PikasialFS;
import pikasial.images.tags.*;

public class MetaParser {

    public static final String DTD_PUBLIC_ID = "-//Pikasial G14//DTD Albums Pikasial//FR";
    public static final String DTD_SYSTEM_ID = "http://ben.roussel1.free.fr/pikasial/pikasial.dtd";
    private static MetaParser parser = new MetaParser();
    private DocumentBuilderFactory factory;
    private DocumentBuilder builder;

    private MetaParser() {
        try {
            factory = DocumentBuilderFactory.newInstance();
            builder = factory.newDocumentBuilder();
            builder.setEntityResolver(new PikasialEntityResolver());
        } catch (ParserConfigurationException pce) {
            pce.printStackTrace();
        }
    }

    public void parseAlbumsFile() throws IOException, SAXException {
        String s = Configuration.getInstance().getProperty(Configuration.ALBUMS_FILE_PROP);
        File confFile = (s!=null)?new File(s):new File(Configuration.DEF_ALBUMS_FILE);
        if (!confFile.exists())
        	return;
        Document doc = builder.parse(confFile);
        Element root = doc.getDocumentElement();
        NodeList children = root.getChildNodes();
        Node album, image;
        for (int i = 0; i < children.getLength(); i++) {
            album = children.item(i);
            if (album instanceof Element) {
            	boolean afficher = false;
            	boolean dossier = false;
            	String typ = ((Element) album).getAttribute("type");
            	dossier = (typ != null) && typ.equals("DOSSIER");
            	String aff = ((Element) album).getAttribute("afficher");
            	afficher = (typ != null) && aff.equals("TRUE");
            	NodeList nl = album.getChildNodes();
            	Node tmp;
            	ImageContainer ap;
            	Collection<PikImage> images = new java.util.ArrayList<PikImage>();
            	String nom = null, date = null,comment = null;

            	for (int j = 1; j < nl.getLength(); j++) {
            		tmp = nl.item(j);
            		if (tmp instanceof Element) {
            			Element e = (Element) tmp;
            			String tag = e.getTagName();
            			if (tag.equals("nom")) {
            				nom = ((Text) e.getFirstChild()).getData().trim();
            			} else if (tag.equals("date_creation")) {
            				date = ((Text) e.getFirstChild()).getData().trim();
            			} else if (tag.equals("comment")) {
            				comment = ((Text) e.getFirstChild()).getData().trim();
            			} else if (tag.equals("image")) {
            				image = e;
            				String nomImg = null, pathImg = null, commentImg = null;
            				Collection<Tag> tagsC = new java.util.ArrayList<Tag>();
            				NodeList carac = image.getChildNodes();
            				for (int k = 0; k < carac.getLength(); k++) {
            					Node tmp2 = carac.item(k);
            					if (tmp2 instanceof Element) {
            						Element e2 = (Element) tmp2;
            						String tag2 = e2.getTagName();
            						if (tag2.equals("nom")) {
            							nomImg = ((Text) e2.getFirstChild()).getData().trim();
            						} else if (tag2.equals("chemin")) {
            							pathImg = ((Text) e2.getFirstChild()).getData().trim();
            						} else if (tag2.equals("comment")) {
            							commentImg = ((Text) e2.getFirstChild()).getData().trim();
            						} else if (tag2.equals("tags")) {
            							String tagName, tagType;
            							NodeList tags = e2.getChildNodes();
            							for (int l = 0; l < tags.getLength(); l++) {
            								Node tmp3 = tags.item(l);
            								if (tmp3 instanceof Element) {
            									Element e3 = (Element) tmp3;
            									if (e3.getTagName().equals("tag")) {
            										tagName = ((Text) e3.getFirstChild()).getData().trim();
            										tagType = e3.getAttribute("type");
            										if (tagType == null) {
            											tagType = "PERSO";
            										}
            										if (tagType.equals("PERSO"))
            											tagsC.add(new TagPerso(tagName));
            										else if (tagType.equals("EVENT"))
            											tagsC.add(new TagEvenement(tagName));
            										else if (tagType.equals("PLACE"))
            											tagsC.add(new TagLieu(tagName));
            										else if (tagType.equals("PERSON"))
            											tagsC.add(new TagPersonne(tagName));
            									} else {
            										throw new SAXException("Tag inconnu : " + e3.getTagName());
            									}
            								}
            							}
            						} else {
            							throw new SAXException("Tag inconnu : " + tag2);
            						}
            					}
            				}
            				PikImage pik = new PikImage(pathImg);
            				pik.setName(nomImg);
            				if (commentImg != null)
            					pik.setCommentaire(commentImg);
            				if (!tagsC.isEmpty()) {
            					for (Tag t : tagsC) {
            						System.out.println("ya des tags");
            						if (t instanceof TagPerso)
            							pik.getTags().addTagPerso((TagPerso) t);
            						else if (t instanceof TagPersonne)
            							pik.getTags().setTagPersonne((TagPersonne) t);
            						else if (t instanceof TagEvenement)
            							pik.getTags().setTagEvenement((TagEvenement) t);
            						else if (t instanceof TagLieu)
            							pik.getTags().setTagLieu((TagLieu) t);
            					}
            				}
            				images.add(pik);
            			} else {
            				throw new SAXException("Tag inconnu " + tag);
            			}
            		}
            	}
            	if (dossier) {
            		ap = new Dossier(new File(nom));
            		ap.addAllImages(images);
                        for (PikImage pikim : images) {
                            pikasial.fs.PikasialFS.getInstance().add(pikim, false);
                        }
            		ListeAlbums.getInstance().ajouterDossier((Dossier) ap);
            	} else {
            		try {
            			ap = (date != null)?new AlbumPhotos(nom,new Date(Long.parseLong(date))):new AlbumPhotos(nom);
            			ap.addAllImages(images);
            			ListeAlbums.getInstance().ajouterAlbum((AlbumPhotos) ap);
            		} catch (NumberFormatException nfe) {
            			throw new SAXException("Format de date invalide dans le fichier album.xml : "+date);
            		}
            	}
            	if (afficher) {
                      ListeAlbums.getInstance().setAlbumAffiche(ap);
                      if (ap instanceof Dossier) {
                          PikasialFS.getInstance().notifyObservers(PikasialFS.getInstance().findPath((Dossier)ap));
                      }
                }
            }
        }
    }

    public static MetaParser getInstance() {
        return parser;
    }

    public Document createAlbumsDOMTree() {
        Document doc = builder.newDocument();
        Collection<ImageContainer> collec = new java.util.ArrayList<ImageContainer>();
        collec.addAll(ListeAlbums.getInstance().getAllAlbums());
        collec.addAll(ListeAlbums.getInstance().getAllDossiers());

        // Code de génération
        Element pikasial = doc.createElement("pikasial");
        doc.appendChild(pikasial);
        pikasial.appendChild(doc.createComment("Fichier contenant les informations relatives"
            + " aux albums de Pikasial. NE PAS MODIFIER !!!"));
        Element album;
        for (ImageContainer ic : collec) {
            album = doc.createElement("album");
            album.setAttribute("type",(ic instanceof AlbumPhotos)?"ALBUM":"DOSSIER");
            if (ListeAlbums.getInstance().getAlbumAffiche() == ic)
            	album.setAttribute("afficher","TRUE");
            pikasial.appendChild(album);

            Element name = doc.createElement("nom");
            String str = (ic instanceof AlbumPhotos)?((AlbumPhotos) ic).getName():((Dossier) ic).getPath();
            //System.out.println(str);
            name.setTextContent(str);
            album.appendChild(name);
            if (ic instanceof AlbumPhotos) {
            	Date d =((AlbumPhotos) ic).getDateCreation();
                Element dateCreat = doc.createElement("date_creation");
                dateCreat.setTextContent(""+d.getTime());
                album.appendChild(dateCreat);
            }

            Element image;
            for (PikImage pik : ic.getAllImages()) {
                image = doc.createElement("image");
                album.appendChild(image);

                Element imgNom = doc.createElement("nom");
                imgNom.setTextContent(pik.getName());
                image.appendChild(imgNom);

                Element path = doc.createElement("chemin");
                path.setTextContent(pik.getPath());
                image.appendChild(path);

                if (pik.getCommentaire() != null) {
                		Element commentaire = doc.createElement("commentaire");
                		commentaire.setTextContent(pik.getCommentaire());
                		image.appendChild(commentaire);
                }

                Collection<Tag> tags = pik.getTags().getAllTags();
                if (!tags.isEmpty()) {
                	Element tagss = doc.createElement("tags");
                	image.appendChild(tagss);
                	Element tag;
                	for (Tag t : tags) {
                		tag = doc.createElement("tag");
                		tag.setTextContent(t.getDescription());
                		if (t instanceof TagPerso)
                			tag.setAttribute("type","PERSO");
                		else if (t instanceof TagPersonne)
                			tag.setAttribute("type","PERSON");
                		else if (t instanceof TagEvenement)
                			tag.setAttribute("type","EVENT");
                		else if (t instanceof TagLieu)
                			tag.setAttribute("type","PLACE");
                		tagss.appendChild(tag);
                	}
                }
            }
        }
        return doc;
    }

    public void saveAlbumList(Document doc) throws TransformerException, IOException {
        String s = Configuration.getInstance().getProperty(Configuration.ALBUMS_FILE_PROP);
        File confFile = (s!=null)?new File(s):new File(Configuration.DEF_ALBUMS_FILE);
        Transformer t = TransformerFactory.newInstance().newTransformer();
        t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, DTD_SYSTEM_ID);
        t.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, DTD_PUBLIC_ID);
        t.setOutputProperty(OutputKeys.INDENT, "yes");
        t.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(confFile)));
    }

    class PikasialEntityResolver implements EntityResolver {
        public InputSource resolveEntity(String publicID, String systemID)
            throws SAXException, IOException {
            if (publicID.equals(DTD_PUBLIC_ID) ||
                systemID.equals(DTD_SYSTEM_ID)) {
                File f;
                URL url = getClass().getResource("/divers/pikasial.dtd");
                try {
                    f = new File(url.toURI());
                } catch(URISyntaxException e) {
                    f = new File(url.getPath());
                }
                return new InputSource(new FileInputStream(f));
            } else {
                return null;
            }
        }
    }
}
